#include "Defs.h"
#include "Buffer.h"


unsigned char Buffer_T::pbUTF8_AND[6][6] =
{
	{0x80, 0,    0,    0,    0,    0   },
	{0xE0, 0xC0, 0,    0,    0,    0   },
	{0xF0, 0xC0, 0xC0, 0,    0,    0   },
	{0xF8, 0xC0, 0xC0, 0xC0, 0,    0   },
	{0xFC, 0xC0, 0xC0, 0xC0, 0xC0, 0   },
	{0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0}
};


unsigned char Buffer_T::pbUTF8_CMP[6][6] =
{
	{0x00, 0,    0,    0,    0,    0   },
	{0xC0, 0x80, 0,    0,    0,    0   },
	{0xE0, 0x80, 0x80, 0,    0,    0   },
	{0xF0, 0x80, 0x80, 0x80, 0,    0   },
	{0xF8, 0x80, 0x80, 0x80, 0x80, 0   },
	{0xFC, 0x80, 0x80, 0x80, 0x80, 0x80}
};

Buffer_T::Buffer_T()
	:_pb(0),
	_len(0),
	_max(0),
	_isSymlink(true)
{}

Buffer_T::Buffer_T(unsigned char * pb, unsigned int len)
	:_pb(pb),
	_len(len),
	_max(len),
	_isSymlink(true)
{}

Buffer_T::Buffer_T(const char * sz)
	:_pb((unsigned char *)sz),
	_len(lstrlen(sz)),
	_max(lstrlen(sz)),
	_isSymlink(true)
{}

Buffer_T::Buffer_T(const String_T & str)
	:_pb((unsigned char *)str.Ptr()),
	_len(str.Length()),
	_max(str.Length()),
	_isSymlink(true)
{}

Buffer_T::Buffer_T(const Buffer_T & buf)
{
	if( &buf != this )
	{
		if( buf._isSymlink )
		{
			_isSymlink = true;
			_pb = buf._pb;
			_len = buf._len;
			_max = buf._max;
		}
		
		else
		{
			_isSymlink = false;
			_pb = 0;
			_len = buf._len;
			_max = buf._max;

			if( buf._max )
			{
				_pb = (unsigned char *)Memory_T::Alloc(buf._max * sizeof(unsigned char));
				RtlMoveMemory(_pb, buf._pb, buf._max);
			}
		}
	}
}

Buffer_T & Buffer_T::operator=(const Buffer_T & buf)
{
	if( &buf != this )
	{
		Free();
		
		if( buf._isSymlink )
		{
			_isSymlink = true;
			_pb = buf._pb;
			_len = buf._len;
			_max = buf._max;
		}
		
		else
		{
			_isSymlink = false;
			_pb = 0;
			_len = buf._len;
			_max = buf._max;

			if( buf._max )
			{
				_pb = (unsigned char *)Memory_T::Alloc(buf._max * sizeof(unsigned char));
				RtlMoveMemory(_pb, buf._pb, buf._max);
			}
		}
	}

	return *this;
}

Buffer_T::~Buffer_T()
{
	Free();
}

void Buffer_T::Free()
{
	if( !_isSymlink && _pb )
	{
		FREE(_pb);
	}

	_pb = 0;
	_len = 0;
	_max = 0;
	_isSymlink = true;
}

bool Buffer_T::Equals(const Buffer_T & buf, bool isCaseSensitive) const
{
	if( !buf._pb || (buf._len != _len) )
	{
		return false;
	}

	return 
		(isCaseSensitive && !memcmp(_pb, buf._pb, buf._len)) || 
		(!isCaseSensitive && !_memicmp(_pb, buf._pb, buf._len));
}

bool Buffer_T::StartsWith(const Buffer_T & buf, bool isCaseSensitive) const
{
	if( !buf._pb )
	{
		return false;
	}

	if( (buf._len == 0) || (buf._len > _len) )
	{
		return false;
	}

	if( isCaseSensitive )
	{
		return !memcmp(_pb, buf._pb, buf._len);
	}

	return !_memicmp(_pb, buf._pb, buf._len);
}

unsigned int Buffer_T::IndexOf(const Buffer_T & buf) const
{
	return IndexOf(buf, 0);
}

unsigned int Buffer_T::IndexOf(const Buffer_T & buf, unsigned int startIndex) const
{
	if( !_pb || !buf._pb || (buf._len == 0) || (buf._len > _len) )
	{
		return _len;
	}

	for( unsigned int iSeek = startIndex ; iSeek < (_len - buf._len + 1) ; iSeek++ )
	{
		if( !memcmp(buf._pb, _pb + iSeek, buf._len) )
		{
			return iSeek;
		}
	}

	return _len;
}

unsigned int Buffer_T::LastIndexOf(const Buffer_T & buf) const
{
	if( !_pb || !buf._pb )
	{
		return _len;
	}

	if( !buf._len || (buf._len > _len) )
	{
		return _len;
	}

	for( unsigned int iSeek = (_len - buf._len) ; iSeek > 0 ; iSeek-- )
	{
		if( !memcmp(_pb + iSeek, buf._pb, buf._len) )
		{
			return iSeek;
		}
	}

	if( !memcmp(_pb, buf._pb, buf._len) )
	{
		return 0;
	}

	return _len;
}

Buffer_T Buffer_T::SubBuffer(unsigned int startIndex) const
{
	return Buffer_T(_pb + startIndex, _len - startIndex);
}

Buffer_T Buffer_T::SubBuffer(unsigned int startIndex, unsigned int length) const
{
	return Buffer_T(_pb + startIndex, length);
}

void Buffer_T::Create(unsigned int max)
{
	Free();

	_pb = (unsigned char *)Memory_T::Alloc(max * sizeof(unsigned char));
	_isSymlink = false;
	_max = max;
	_len = max;
}

void Buffer_T::Concat(const Buffer_T & buf)
{
	if( !buf._pb || (buf._len == 0) )
	{
		return;
	}

	unsigned int max = _max + buf._max;
	unsigned int len = _len + buf._len;

	void * pv = Memory_T::Alloc(max * sizeof(unsigned char));

	if( _len )
	{
		RtlMoveMemory((unsigned char *)pv, _pb, _len);
	}

	RtlMoveMemory((unsigned char *)pv + _len, buf._pb, buf._max);

	Free();

	_pb = (unsigned char *)pv;
	_len = len;
	_max = max;
	_isSymlink = false;
}

bool Buffer_T::IsUtf8Encoded
(
	unsigned char pbA[],
	unsigned char pbB[],
	unsigned char pbC[],
	unsigned char pbD[],
	char cMax
)
{
	for( char c = cMax ; c >= 0 ; c-- )
	{
		if( (pbA[c] & pbB[c]) != pbC[c] )
			return false;
		pbD[c] = pbA[c] & ~pbB[c];
	}

	return true;
}

void Buffer_T::Decode()
{
	unsigned char pbRes[6];
	unsigned char pbSum[4];
	char cIndex, cUtf;
	bool fEncoded;
	int ix, len, hex;
	char szHex2[3];
	char szHex4[5];
	char * szStop;

	hex = 0;
	ix = 0;
	len = 0;

	szHex2[2] = '\0';
	szHex4[4] = '\0';
	len = _len;
	_len = 0;

	for( ix = 0 ; ix < len ; ix++ )
	{
		if( _pb[ix] != '%' )
			_pb[_len++] = _pb[ix];

		else if( ((ix + 5) < len) && ((_pb[ix + 1] == 'u') || (_pb[ix + 1] == 'U')) )
		{
			szHex4[0] = _pb[ix + 2];
			szHex4[1] = _pb[ix + 3];
			szHex4[2] = _pb[ix + 4];
			szHex4[3] = _pb[ix + 5];
			hex = strtol(szHex4, &szStop, 16);
			if( szStop == (szHex4 + 4) )
			{
				if( hex >= 0xFF00 )
					hex -= 0xFEE0;
				if( hex > 0x00FF )
					_pb[_len++] = (unsigned char)(hex >> 8);
				_pb[_len++] = (unsigned char)(hex & 0x00FF);
				ix += 5;
			}
		}

		else if( (ix + 2) < len )
		{
			szHex2[0] = _pb[ix + 1];
			szHex2[1] = _pb[ix + 2];
			hex = strtol(szHex2, &szStop, 16);
			if( szStop == (szHex2 + 2) )
			{
				_pb[_len++] = (unsigned char)hex;
				ix += 2;
			}
		}

		else
			_pb[_len++] = _pb[ix];
	}

	len = _len;
	_len = 0;

	for( ix = 0 ; ix < len ; ix++ )
	{
		fEncoded = false;
		for( cUtf = 0 ; ((ix + cUtf) < len) && (cUtf < 6) && !fEncoded ; cUtf++ )
		{
			fEncoded = IsUtf8Encoded(_pb + ix, Buffer_T::pbUTF8_AND[cUtf], Buffer_T::pbUTF8_CMP[cUtf], pbRes, cUtf);
			if( fEncoded )
			{
				pbSum[0] = 0x00;
				pbSum[1] = 0x00;
				pbSum[2] = 0x00;
				pbSum[3] = 0x00;

				for( cIndex = cUtf ; cIndex >= 0 ; cIndex-- )
				{
					if( cIndex == cUtf )
					{
						pbSum[3] = pbSum[3] + pbRes[cIndex];
					}
					else if( (cIndex + 1) == cUtf )
					{
						pbSum[3] += pbRes[cIndex] << 6;
						pbSum[2] += (pbRes[cIndex] >> 2) << 8;
					}
					else if( (cIndex + 2) == cUtf )
					{
						pbSum[2] += (pbRes[cIndex] << 4) << 8;
						pbSum[1] += (pbRes[cIndex] >> 4) << 16;
					}
					else if( (cIndex + 3) == cUtf )
					{
						pbSum[1] += (pbRes[cIndex] << 2) << 16;
					}
					else if( (cIndex + 4) == cUtf )
					{
						pbSum[0] += (pbRes[cIndex]) << 24;
					}
					else if( (cIndex + 5) == cUtf )
					{
						pbSum[0] += (pbRes[cIndex] << 6) << 24;
					}
				}

				// Jump over the bytes encoded
				ix += cUtf;

				// Check on which byte we should start to dump
				if( pbSum[0] != 0x00 )
					cUtf = 0;
				else if( pbSum[1] != 0x00 )
					cUtf = 1;
				else if( pbSum[2] != 0x00 )
					cUtf = 2;
				else
					cUtf = 3;

				for( cIndex = cUtf ; cIndex < 4 ; cIndex++ )
					_pb[_len++] = pbSum[cIndex];
			}
		}

		if( !fEncoded )
			_pb[_len++] = _pb[ix];
	}
}

bool Buffer_T::IsNumeric() const
{
	for( unsigned int ix = 0 ; ix < _len ; ix++ )
	{
		if( (_pb[ix] < '0') || (_pb[ix] > '9') )
		{
			return false;
		}
	}

	return true;
}

unsigned int Buffer_T::ToNumber() const
{
	char sz[11];

	if( _len == 0 )
	{
		return 0;
	}

	else if( _len > (sizeof(sz) - 1) )
	{
		RtlMoveMemory(sz, _pb, (sizeof(sz) - 1));
		sz[(sizeof(sz) - 1)] = '\0';
	}
	else
	{
		RtlMoveMemory(sz, _pb, _len);
		sz[_len] = '\0';
	}

	return atoi(sz);
}

const char * Buffer_T::ToString(char * sz, unsigned int len) const
{
	char hex[5];

	unsigned int ix = 0;
	for( unsigned int _ix = 0 ; _ix < _len ; _ix++ )
	{
		if( (_pb[_ix] < 0x20) || (_pb[_ix] > 0x7F) )
		{
			if( (ix + 4) < (len - 1) )
			{
				_snprintf(hex, sizeof(hex) - 1, "\\x%02x", _pb[_ix]);
				RtlMoveMemory(sz + ix, hex, sizeof(hex) - 1);
				ix += 4;
			}
			else
			{
				break;
			}
		}
		else if( ix < (len - 1) )
		{
			sz[ix] = _pb[_ix];
			ix++;
		}
		else
		{
			break;
		}
	}

	sz[ix] = '\0';
	return sz;
}
